﻿using System;
using System.Security.Cryptography;

namespace OpenLS.Spreadsheet.Formats.Biff.Cryptography
{
    public sealed class MD4CryptoServiceProvider : MD4
    {
        private readonly int _handle;
        private bool _disposed;
        private int _hash;

        public MD4CryptoServiceProvider()
        {
            _handle = CAPIProvider.CreateHandle();
            Initialize();
            _disposed = false;
        }

        public override void Initialize()
        {
            if (_disposed)
                throw new ObjectDisposedException(GetType().FullName);
            if (_hash != 0)
                CryptoApi.CryptDestroyHash(_hash);
            CryptoApi.CryptCreateHash(_handle, CryptoApi.CALG_MD4, 0, 0, out _hash);
        }

        protected override void HashCore(byte[] array, int ibStart, int cbSize)
        {
            if (_disposed)
                throw new ObjectDisposedException(GetType().FullName);
            var copy = new byte[cbSize];
            Array.Copy(array, ibStart, copy, 0, cbSize);
            if (CryptoApi.CryptHashData(_hash, copy, copy.Length, 0) == 0)
                throw new CryptographicException();
        }

        protected override byte[] HashFinal()
        {
            if (_disposed)
                throw new ObjectDisposedException(GetType().FullName);
            var buffer = new byte[16];
            int length = buffer.Length;
            if (CryptoApi.CryptGetHashParam(_hash, CryptoApi.HP_HASHVAL, buffer, ref length, 0) == 0)
                throw new CryptographicException();
            return buffer;
        }

        protected override void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (_hash != 0)
                {
                    CryptoApi.CryptDestroyHash(_hash);
                    _hash = 0;
                }
                try
                {
                    GC.SuppressFinalize(this);
                }
                catch
                {
                }
                _disposed = true;
            }
        }

        ~MD4CryptoServiceProvider()
        {
            Clear();
        }
    }
}